package com.xm.concurrent.base.instance;

import com.xm.concurrent.base.util.SleepUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 4.4.1 等待超时模式
 * 对 等待/通知经典范式 简单修改
 *
 * @author xiaoming
 * @date 2021-05-27 22:21
 */
public class WaitOutTime {
    public static void main(String[] args) {
        // 启动消费者（等待）
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();

        SleepUtils.second(2);
        
        // 启动生产者（通知）
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();


    }


    static boolean flag = true;
    static Object lock = new Object();
    /** 等待持续时间 ，则超时时间为： now + 3  */
    static long remaining = 1000*1;
    /** 超时时间 */
    static long future = System.currentTimeMillis() + remaining;

    /**
     * 等待方 (消费者)
     */
    static class Wait implements Runnable {
        @Override
        public void run() {
            // 1. 获取对象锁
            synchronized (lock) {
                // 超时时间需要大于 0 才能超时， 小于零则表示不用等待

                // 2. 条件不满足循环
                while (flag && remaining >0) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true . wait （消费者等待） @ "
                                + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        // 等待
//                        lock.wait();
                        // 锁的处理
                        lock.wait(remaining);

                        remaining = future - System.currentTimeMillis();
                    } catch (InterruptedException e) {
                        System.out.println("error: "+ e.getMessage());
                    }
                }

                // 3. 条件， 对应逻辑处理
                // 条件满足, 完成工作
                System.out.println(Thread.currentThread() + " flag is false, running （消费者感知变化， 处理逻辑）@"
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }

      


        }
    }

    /**
     * 通知方 (生产者)
     */
    static class Notify implements Runnable {
        @Override
        public void run() {
            /** 1. 获取对象锁 */
            synchronized (lock) {
                // 获取锁, 然后进行通知 ( 通知时不释放锁)
                // 直到释放 lock 后, WaitThread 才能从 wait 方法中返回
                System.out.println(Thread.currentThread() + " hold lock. notify （生产者通知）@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));

                /** 3。 通知所有等待 */
                // 使用时需要先对对象加锁， 释放锁
                lock.notifyAll();
                /** 2。改变条件 */
                flag = false;
                SleepUtils.second(5);
            }

            // 再次加锁
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. sleep （生产者再次加锁）@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtils.second(5);
            }

        }
    }


}
